iT邦幫忙

2024 iThome 鐵人賽

DAY 15
1
Modern Web

Dive into CSS Challenge:從問題到解決方案的實踐之旅系列 第 15

Day 15 - CSS Challenge #9:Rainy Night - Weather UI (中)

  • 分享至 

  • xImage
  •  

題目

CSS Challenge Day9
https://ithelp.ithome.com.tw/upload/images/20240926/20169403Uu6JEZvXsn.png

題目是一個看起來像是 Weather Widget 的介面,配上下雨的動畫,並且在初次 loading 時,月亮會有升起的動畫。

上面的圖是題目,而我們要做出幾乎一樣的樣子,題目中還有附上出題官方的CodePen,也有附上給我們解題用的template,當我們真的不會的時候,還是可以參考他們的寫法,所以沒有想像中困難。

我做好的此題CSS Challeage解答

那麼我們就開始吧。

題目分析

這個題目要求我們製作一個天氣 Widget 的畫面

  • 分為上下兩個區塊
    1. 上方圖片
    2. 下方天氣資訊
  • 圖片分為三個部分
    1. 月亮及動畫(初始時由下方升起)
    2. 夜空與後面的山坡地
    3. 下雨的動畫

前情提要

我們上次已經做完 下方天氣資訊 的部分,長這樣:
https://ithelp.ithome.com.tw/upload/images/20240928/20169403UQtRNabdg5.png

那我們今天先來做上方圖片的部分,這部分我會拆三個地方來寫。

  1. 月亮及動畫(初始時由下方升起)
  2. 夜空與後面的山坡地
  3. 下雨的動畫

開始解題

月亮及動畫

基礎架構

.frame
	.view
        // ───新增的部分
		.moon
			- for i in (1..11)
				div class="crater-#{i}"
        // ───新增的部分
	.label
    ...

首先,一樣先開版,把結構作出來。
這邊很簡單的,我就先增加一個 div 取名為 .moon

.moon 的作用是生成 11 個 crater 元素,每個元素的類名依次為 crater-1crater-11
通過 for 迴圈來簡化生成多個 div 的過程,讓我們可以輕鬆建立重複的元素,這段就是在建立月球表面的隕石坑。

這樣的寫法方便快速添加多個具有相似樣式但需要獨立標識的元素,以便後續使用不同的 CSS 或 JavaScript 來針對不同的 crater 進行樣式或行為上的定制化操作。

由此可知,這段就是我們建立了一個名為 .moondiv,而上面建立了 11 個 div,它們是依次名為 crater-1crater-11 的隕石坑。

月亮樣式

$moonColor: #F6EDBD;
$moonWH: 67px;

.view {
    ...
    .moon {
        width: $moonWH;
		height: $moonWH;
		position: absolute;
		background: $moonColor;
		border-radius: 50%;
		overflow: hidden;
		top: 45px;
		left: 55px;
		box-shadow: 0 0 10px 0 $moonColor;
		animation: animate-rise 1s ease-out;
    }
}

用變數設定月亮的寬高跟顏色,顏色的部分一樣是使用第一天教的小工具來吸色。

  • 大小與位置:widthheight 設定了月球的大小,position: absolute 將其定位,並通過 topleft 調整它的位置。
    這邊要記得,我們設定的位置是月亮最終的位置,而不用設定動畫一開始時,在地平線下面的位置。
  • 外觀:使用 border-radius: 50% 將其變為圓形,並賦予背景色 (background: $moonColor),再加上陰影效果 (box-shadow),這段陰影是為了讓月亮有外面的光暈。
  • 動畫:應用了 animate-rise 1s ease-out,讓月球產生緩慢升起的效果。

https://ithelp.ithome.com.tw/upload/images/20240929/20169403BS2Nn7LeU6.png

月亮動畫

接著我們來製作動畫,讓它從底下升起。

@keyframes animate-rise {
	from {
		transform: translate(-20px,200px);
	}
	to {
		transform: translate(0,0px);
	}
}

我們剛剛已經在 CSS 中設定動畫的名字要叫做 animate-rise ,所以我們就去設定一個 @keyframes 叫做 animate-rise

這邊要做的就是把它在地平線底下的位置定義出來,然後讓它藉由 fromto,自然的去移動位置,最終移動到我們剛剛已經設定好的最終月亮位置。這樣月亮升起的動畫就完成了。

隕石坑樣式

.view {
    ...
    .moon {
        ...
        // crater
		.crater {
			&-1 {
				position: absolute;
				width: 11px;
				height: 11px;
				top: 9px;
				left: 28px;
				border-radius: 10px;
				background: #ECE1A8;
			}
			&-2 {
				@extend .crater-1;
				top: 12px;
				left: 0;
			}
            ...
        }
    }
}

我在這裡先定義了 .crater-1 的樣式。

使用 position: absolute 定位在 topleft 位置。其大小和圓角通過 widthheightborder-radius 控制,並賦予了淡黃色的背景色 (background: #ECE1A8),模擬月球表面的坑洞效果。

@extend 用來繼承其他隕石坑的樣式,這樣可以避免重複代碼,並針對特定的隕石坑進行細微的調整,如位置 (top, left) 和大小 (width, height) 等。

這邊我是一個個微調每個坑洞的所在位置跟大小,需要花比較多時間自行調整。在調整大小的時候,可以先把動畫那行註解掉,就不會一直跑動畫。

我在這邊就只貼出部分隕石坑的代碼,要看更詳細的可以去我的 codepen 察看。

隕石坑都調整好了之後會長這樣:
https://ithelp.ithome.com.tw/upload/images/20240929/20169403EeM5xZnXPJ.png


山坡地

基礎架構

https://ithelp.ithome.com.tw/upload/images/20240929/20169403soHGHOANu1.png
從題目可以看出,背景的山坡地其實分成了前後兩個部分,兩塊的顏色也不太一樣。
所以這邊我們就分兩個部分來寫。

.frame
	.view
		.moon
            ...
        // ───新增的部分
        - for i in (1..2)
			div class="ground-bg-#{i}"
		- for i in (1..3)
			div class="ground-front-#{i}"
        // ───新增的部分
	.label
    ...

這邊我們使用迴圈在 HTML 中生成 ground-bgground-front 的元素。每個元素的 class 名稱根據迴圈計數器動態生成。

  • for i in (1..2):這是生成兩個 .ground-bg 元素,class 分別是 ground-bg-1ground-bg-2
  • for i in (1..3):這是生成三個 .ground-front 元素,class 分別是 ground-front-1ground-front-2ground-front-3

山坡地樣式

$groundBG_Color: #26314E;
$groundFront_Color: #303C5D;

	.ground {
		&-bg {
			&-1 {
				position: absolute;
				width: 337px;
				height: 280px;
				top: 201px;
				left: -57px;
				background-color: $groundBG_Color;
				border-radius: 50%;
			}
			&-2 {
				@extend .ground-bg-1;
				top: 197px;
				left: 177px;
			}
		}
		&-front {
			...
		}
	}

首先,我先使用變數定義好前後兩個山坡地的背景顏色,接著我們先來寫後面的山坡地。

山坡地的圓形其實就是一個大的圓,但我們只顯示它們的邊角一部分,其餘的部分在 .frameoverflow: hidden 的作用下就會隱藏掉。

我們讓,.ground 包含兩個子元素:.ground-bg.ground-front,並使用 Sass 的 @extend 功能來複製樣式:

.ground-bg 定義了兩個背景元素,分別是使用ground-bg-1ground-bg-2,我們使用 position: absolute 來控制位置和大小,並有不同的 topleft 屬性調整位置,background-color

ground-bg-2 extend 了 ground-bg-1 的樣式,所以可以減少重複的程式碼,我們只需要修改他的位置就可以了。

接著我們來寫前面的山坡地:

	.ground {
		&-bg {
            ...
		}
		&-front {
			&-1 {
				@extend .ground-bg-1;
				background: $groundFront_Color;
				top: 248px;
				left: -136px;
			}
			&-2 {
				@extend .ground-front-1;
				top: 220px;
				left: 65px;
			}
            ...
		}
	}

.ground-front 則擴展了 .ground-bg 的樣式,並且改變了顏色和位置來形成前景圖層。

這邊一樣將 ground-front-2ground-front-3 都去 extend .ground-front-1 的樣式,如此變可以不用重複寫背景色。再修改一下各自的位置,背景的山坡地就完成了。

https://ithelp.ithome.com.tw/upload/images/20240929/201694036fkAFwp0kz.png

由於篇幅太長了,我決定再分篇寫,下一篇再來寫後面的 下雨的動畫。


Wrap up and go home

希望改變了這種按照步驟的寫法,能讓更多人看得懂,也能跟我一樣喜歡上寫CSS。

那今天就先到這裡,明天我們再繼續來玩下一集。


上一篇
Day 14 - CSS Challenge #9:Rainy Night - Weather UI (上)
下一篇
Day 16 - CSS Challenge #9:Rainy Night - Weather UI (下)
系列文
Dive into CSS Challenge:從問題到解決方案的實踐之旅30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言